package org.ofbiz.osgi.container;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

import org.apache.felix.framework.Felix;
import org.apache.felix.framework.util.FelixConstants;
import org.ofbiz.base.container.Container;
import org.ofbiz.base.container.ContainerException;
import org.ofbiz.base.util.Debug;
import org.ofbiz.osgi.activator.HostActivator;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.launch.Framework;
import org.osgi.util.tracker.ServiceTracker;

/**
 * http://felix.apache.org/site/apache-felix-framework-launching-and-embedding.
 * html
 * 
 * @author chatree
 * 
 */
public class OSGIContainer implements Container {
    
    public final static String module = OSGIContainer.class.getName();
    
    private String name;
    private Framework framework;
    private File cacheDir;
    private HostActivator hostActivator;
    private Map<String, Object> lookupServiceMap = new HashMap<String, Object>();
    private ServiceTracker serviceTracker;
    
    private static final class BootClassLoader extends ClassLoader {
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            if (name.equals("boot.test.Test")) {
                return String.class;
            }
            throw new ClassNotFoundException();
        }
    }
    
    private static File createBundle(String manifest) throws IOException {
        File f = File.createTempFile("ofbiz-bundle", ".jar");
        f.deleteOnExit();
        
        Manifest mf = new Manifest(new ByteArrayInputStream(
                manifest.getBytes("utf-8")));
        mf.getMainAttributes().putValue("Manifest-Version", "1.0");
        JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf);
        os.close();
        
        return f;
    }
    
    @Override
    public void init(String[] args, String name, String configFile)
            throws ContainerException {
        this.name = name;
    }
    
    @Override
    public boolean start() throws ContainerException {
        
        final ClassLoader bootClassloader = new BootClassLoader();
        final List<Object> loadedObjects = new ArrayList<Object>();
        
        @SuppressWarnings("serial")
        Map<Object, Object> bundleClassloaders = new HashMap<Object, Object>() {
            public Object get(Object o) {
                loadedObjects.add(o);
                return bootClassloader;
            }
        };
        
        Map<String, Object> params = new HashMap<String, Object>();
        params.put(Constants.FRAMEWORK_SYSTEMPACKAGES,
                "org.osgi.framework; version=1.4.0,"
                        + "org.osgi.service.packageadmin; version=1.2.0,"
                        + "org.osgi.service.startlevel; version=1.1.0,"
                        + "org.osgi.util.tracker; version=1.3.3,"
                        + "org.osgi.service.url; version=1.0.0");
        params.put(FelixConstants.BOOT_CLASSLOADERS_PROP, bundleClassloaders);
        
        // Export the host provided service interface package.
        hostActivator = new HostActivator(lookupServiceMap);
        List<BundleActivator> bundleActivators = new LinkedList<BundleActivator>();
        bundleActivators.add(hostActivator);
        params.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA,
                "org.ofbiz.osgi.service.lookup; version=1.0.0");
        params.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP,
                bundleActivators);
        
        try {
            cacheDir = File.createTempFile("ofbiz-cache", ".dir");
            cacheDir.delete();
            cacheDir.mkdirs();
            String cacheDirPath = cacheDir.getPath();
            params.put("felix.cache.profiledir", cacheDirPath);
            params.put("felix.cache.dir", cacheDirPath);
            params.put(Constants.FRAMEWORK_STORAGE, cacheDirPath);
            
            String mf = "Bundle-SymbolicName: ofbiz\n"
                    + "Bundle-Version: 1.1.0\n" + "Bundle-ManifestVersion: 2\n"
                    + "Export-Package: org.ofbiz.osgi";
            File bundleFile = createBundle(mf);
            
            framework = new Felix(params);
            framework.init();
            framework.getBundleContext().installBundle(
                    bundleFile.toURI().toString());
            framework.start();
            
            serviceTracker = new ServiceTracker(hostActivator.getContext(),
                    String.class.getName(), null);
            serviceTracker.open();
            
            return true;
        } catch (IOException e) {
            String errMsg = "Cound not create temp cache direcetory.";
            Debug.logError(e, errMsg, module);
            throw new ContainerException(errMsg);
        } catch (BundleException e) {
            String errMsg = "Cound not install OFBiz bundle.";
            Debug.logError(e, errMsg, module);
            throw new ContainerException(errMsg);
        }
    }
    
    @Override
    public void stop() throws ContainerException {
        try {
            framework.stop();
            framework.waitForStop(0);
        } catch (BundleException e) {
            String errMsg = "Cound not stop framework.";
            Debug.logError(e, errMsg, module);
            throw new ContainerException(errMsg);
        } catch (InterruptedException e) {
            String errMsg = "Cound not wait for stop framework.";
            Debug.logError(e, errMsg, module);
            throw new ContainerException(errMsg);
        }
    }
    
    @Override
    public String getName() {
        return name;
    }
    
}
